{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python从设计之初就已经是一门面向对象的语言，正因为如此，在Python中创建一个类和对象是很容易的。本节我们将介绍Python的面向对象编程。\n",
    "\n",
    "如果你以前没有接触过面向对象的编程语言，那你可能需要先了解一些面向对象语言的一些基本特征，在头脑里头形成一个基本的面向对象的概念，这样有助于你更容易的学习Python的面向对象编程。\n",
    "\n",
    "接下来我们先来简单的了解下面向对象的一些基本特征。\n",
    "\n",
    "# 面向对象技术简介\n",
    "\n",
    "* 类(Class): 用来描述具有相同的属性和方法的对象的集合。它定义了该集合中每个对象所共有的属性和方法。对象是类的实例。\n",
    "* 方法：类中定义的函数。\n",
    "* 类变量：类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。\n",
    "* 数据成员：类变量或者实例变量用于处理类及其实例对象的相关的数据。\n",
    "* 方法重写：如果从父类继承的方法不能满足子类的需求，可以对其进行改写，这个过程叫方法的覆盖（override），也称为方法的重写。\n",
    "* 实例变量：定义在方法中的变量，只作用于当前实例的类。\n",
    "* 继承：即一个派生类（derived class）继承基类（base class）的字段和方法。继承也允许把一个派生类的对象作为一个基类对象对待。例如，有这样一个设计：一个Dog类型的对象派生自Animal类，这是模拟\"是一个（is-a）\"关系（例图，Dog是一个Animal）。\n",
    "* 实例化：创建一个类的实例，类的具体对象。\n",
    "* 对象：通过类定义的数据结构实例。对象包括两个数据成员（类变量和实例变量）和方法。\n",
    "\n",
    "和其它编程语言相比，Python 在尽可能不增加新的语法和语义的情况下加入了类机制。\n",
    "\n",
    "Python中的类提供了面向对象编程的所有基本功能：类的继承机制允许多个基类，派生类可以覆盖基类中的任何方法，方法中可以调用基类中的同名方法。\n",
    "\n",
    "对象可以包含任意数量和类型的数据。\n"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类定义\n",
    "语法格式如下：\n",
    "\n",
    "```\n",
    "class ClassName:\n",
    "    <statement-1>\n",
    "    .\n",
    "    .\n",
    "    .\n",
    "    <statement-N>\n",
    "```\n",
    "类实例化后，可以使用其属性，实际上，创建一个类之后，可以通过类名访问其属性。\n",
    "\n",
    "类对象\n",
    "类对象支持两种操作：属性引用和实例化。\n",
    "\n",
    "属性引用使用和 Python 中所有的属性引用一样的标准语法：obj.name。\n",
    "\n",
    "类对象创建后，类命名空间中所有的命名都是有效属性名。所以如果类定义是这样:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 14,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "MyClass 类的属性 i 为： 12345\n",
      "MyClass 类的方法 f 输出为： hello world\n"
     ]
    }
   ],
   "source": [
    "class MyClass:\n",
    "    \"\"\"一个简单的类实例\"\"\"\n",
    "    i = 12345   # 所有实例共享数据\n",
    "    def f(self):\n",
    "        self.j = 54321\n",
    "        return 'hello world'  # 每个实例数据\n",
    " \n",
    "# 实例化类\n",
    "x = MyClass()\n",
    " \n",
    "# 访问类的属性和方法\n",
    "print(\"MyClass 类的属性 i 为：\", x.i)\n",
    "print(\"MyClass 类的方法 f 输出为：\", x.f())"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "3.0 -4.5\n"
     ]
    }
   ],
   "source": [
    "class Complex:\n",
    "    def __init__(self, realpart, imagpart):\n",
    "        self.r = realpart\n",
    "        self.i = imagpart\n",
    "x = Complex(3.0, -4.5)\n",
    "print(x.r, x.i)   # 输出结果：3.0 -4.5"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Test object at 0x7f3b644556a0>\n",
      "<class '__main__.Test'>\n"
     ]
    }
   ],
   "source": [
    "class Test:\n",
    "    def prt(self):\n",
    "        print(self)\n",
    "        print(self.__class__)\n",
    " \n",
    "t = Test()\n",
    "t.prt()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "<__main__.Test object at 0x7f3b64455a90>\n",
      "<class '__main__.Test'>\n"
     ]
    }
   ],
   "source": [
    "class Test:\n",
    "    def prt(runoob):\n",
    "        print(runoob)\n",
    "        print(runoob.__class__)\n",
    " \n",
    "t = Test()\n",
    "t.prt()"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "runoob 说: 我 10 岁。\n"
     ]
    }
   ],
   "source": [
    "#类定义\n",
    "class people:\n",
    "    #定义基本属性\n",
    "    name = ''\n",
    "    age = 0\n",
    "    #定义私有属性,私有属性在类外部无法直接进行访问\n",
    "    __weight = 0\n",
    "    #定义构造方法\n",
    "    def __init__(self,n,a,w):\n",
    "        self.name = n\n",
    "        self.age = a\n",
    "        self.__weight = w\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁。\" %(self.name,self.age))\n",
    " \n",
    "# 实例化类\n",
    "p = people('runoob',10,30)\n",
    "p.speak()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 继承"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "Python 同样支持类的继承，如果一种语言不支持继承，类就没有什么意义。派生类的定义如下所示:\n",
    "\n",
    "```\n",
    "class DerivedClassName(BaseClassName1):\n",
    "    <statement-1>\n",
    "    .\n",
    "    .\n",
    "    .\n",
    "    <statement-N>\n",
    "```\n",
    "\n",
    "需要注意圆括号中基类的顺序，若是基类中有相同的方法名，而在子类使用时未指定，python从左至右搜索 即方法在子类中未找到时，从左到右查找基类中是否包含方法。\n",
    "\n",
    "BaseClassName（示例中的基类名）必须与派生类定义在一个作用域内。除了类，还可以用表达式，基类定义在另一个模块中时这一点非常有用:\n",
    "\n"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 7,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "ken 说: 我 10 岁了，我在读 3 年级\n"
     ]
    }
   ],
   "source": [
    "#类定义\n",
    "class people:\n",
    "    #定义基本属性\n",
    "    name = ''\n",
    "    age = 0\n",
    "    #定义私有属性,私有属性在类外部无法直接进行访问\n",
    "    __weight = 0\n",
    "    #定义构造方法\n",
    "    def __init__(self,n,a,w):\n",
    "        self.name = n\n",
    "        self.age = a\n",
    "        self.__weight = w\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁。\" %(self.name,self.age))\n",
    " \n",
    "#单继承示例\n",
    "class student(people):\n",
    "    grade = ''\n",
    "    def __init__(self,n,a,w,g):\n",
    "        #调用父类的构函\n",
    "        people.__init__(self,n,a,w)\n",
    "        self.grade = g\n",
    "    #覆写父类的方法\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁了，我在读 %d 年级\"%(self.name,self.age,self.grade))\n",
    " \n",
    " \n",
    " \n",
    "s = student('ken',10,60,3)\n",
    "s.speak()"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 多重继承\n",
    "\n",
    "Python同样有限的支持多继承形式。多继承的类定义形如下例:\n",
    "\n",
    "```\n",
    "class DerivedClassName(Base1, Base2, Base3):\n",
    "    <statement-1>\n",
    "    .\n",
    "    .\n",
    "    .\n",
    "    <statement-N>\n",
    "```\n",
    "需要注意圆括号中父类的顺序，若是父类中有相同的方法名，而在子类使用时未指定，python从左至右搜索 即方法在子类中未找到时，从左到右查找父类中是否包含方法。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 8,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "我叫 Tim，我是一个演说家，我演讲的主题是 Python\n"
     ]
    }
   ],
   "source": [
    "#类定义\n",
    "class people:\n",
    "    #定义基本属性\n",
    "    name = ''\n",
    "    age = 0\n",
    "    #定义私有属性,私有属性在类外部无法直接进行访问\n",
    "    __weight = 0\n",
    "    #定义构造方法\n",
    "    def __init__(self,n,a,w):\n",
    "        self.name = n\n",
    "        self.age = a\n",
    "        self.__weight = w\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁。\" %(self.name,self.age))\n",
    " \n",
    "#单继承示例\n",
    "class student(people):\n",
    "    grade = ''\n",
    "    def __init__(self,n,a,w,g):\n",
    "        #调用父类的构函\n",
    "        people.__init__(self,n,a,w)\n",
    "        self.grade = g\n",
    "    #覆写父类的方法\n",
    "    def speak(self):\n",
    "        print(\"%s 说: 我 %d 岁了，我在读 %d 年级\"%(self.name,self.age,self.grade))\n",
    " \n",
    "#另一个类，多重继承之前的准备\n",
    "class speaker():\n",
    "    topic = ''\n",
    "    name = ''\n",
    "    def __init__(self,n,t):\n",
    "        self.name = n\n",
    "        self.topic = t\n",
    "    def speak(self):\n",
    "        print(\"我叫 %s，我是一个演说家，我演讲的主题是 %s\"%(self.name,self.topic))\n",
    " \n",
    "#多重继承\n",
    "class sample(speaker,student):\n",
    "    a =''\n",
    "    def __init__(self,n,a,w,g,t):\n",
    "        student.__init__(self,n,a,w,g)\n",
    "        speaker.__init__(self,n,t)\n",
    " \n",
    "test = sample(\"Tim\",25,80,4,\"Python\")\n",
    "test.speak()   #方法名同，默认调用的是在括号中排前地父类的方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 方法重写\n",
    "\n",
    "如果你的父类方法的功能不能满足你的需求，你可以在子类重写你父类的方法，实例如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 9,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "调用子类方法\n",
      "调用父类方法\n"
     ]
    }
   ],
   "source": [
    "class Parent:        # 定义父类\n",
    "   def myMethod(self):\n",
    "      print ('调用父类方法')\n",
    " \n",
    "class Child(Parent): # 定义子类\n",
    "   def myMethod(self):\n",
    "      print ('调用子类方法')\n",
    " \n",
    "c = Child()          # 子类实例\n",
    "c.myMethod()         # 子类调用重写方法\n",
    "super(Child,c).myMethod() #用子类对象调用父类已被覆盖的方法"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "`super()` 函数是用于调用父类(超类)的一个方法。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 类属性与方法\n",
    "\n",
    "类的私有属性\n",
    "__private_attrs：两个下划线开头，声明该属性为私有，不能在类地外部被使用或直接访问。在类内部的方法中使用时 self.__private_attrs。\n",
    "\n",
    "类的方法\n",
    "在类地内部，使用 def 关键字来定义一个方法，与一般函数定义不同，类方法必须包含参数 self，且为第一个参数，self 代表的是类的实例。\n",
    "\n",
    "self 的名字并不是规定死的，也可以使用 this，但是最好还是按照约定是用 self。\n",
    "\n",
    "类的私有方法\n",
    "__private_method：两个下划线开头，声明该方法为私有方法，只能在类的内部调用 ，不能在类地外部调用。self.__private_methods。\n",
    "\n",
    "实例\n",
    "类的私有属性实例如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 11,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "1\n",
      "2\n",
      "2\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'JustCounter' object has no attribute '__secretCount'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-11-eb413b755b77>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     12\u001b[0m \u001b[0mcounter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mcount\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     13\u001b[0m \u001b[0mprint\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mcounter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mpublicCount\u001b[0m\u001b[0;34m)\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 14\u001b[0;31m \u001b[0mprint\u001b[0m \u001b[0;34m(\u001b[0m\u001b[0mcounter\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__secretCount\u001b[0m\u001b[0;34m)\u001b[0m  \u001b[0;31m# 报错，实例不能访问私有变量\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'JustCounter' object has no attribute '__secretCount'"
     ]
    }
   ],
   "source": [
    "class JustCounter:\n",
    "    __secretCount = 0  # 私有变量\n",
    "    publicCount = 0    # 公开变量\n",
    " \n",
    "    def count(self):\n",
    "        self.__secretCount += 1\n",
    "        self.publicCount += 1\n",
    "        print (self.__secretCount)\n",
    " \n",
    "counter = JustCounter()\n",
    "counter.count()\n",
    "counter.count()\n",
    "print (counter.publicCount)\n",
    "print (counter.__secretCount)  # 报错，实例不能访问私有变量"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 12,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "name  :  菜鸟教程\n",
      "url :  www.runoob.com\n",
      "这是公共方法\n",
      "这是私有方法\n"
     ]
    },
    {
     "ename": "AttributeError",
     "evalue": "'Site' object has no attribute '__foo'",
     "output_type": "error",
     "traceback": [
      "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m                            Traceback (most recent call last)",
      "\u001b[0;32m<ipython-input-12-9d93b50ee324>\u001b[0m in \u001b[0;36m<module>\u001b[0;34m()\u001b[0m\n\u001b[1;32m     18\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mwho\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m        \u001b[0;31m# 正常输出\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[1;32m     19\u001b[0m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0mfoo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m        \u001b[0;31m# 正常输出\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0;32m---> 20\u001b[0;31m \u001b[0mx\u001b[0m\u001b[0;34m.\u001b[0m\u001b[0m__foo\u001b[0m\u001b[0;34m(\u001b[0m\u001b[0;34m)\u001b[0m      \u001b[0;31m# 报错\u001b[0m\u001b[0;34m\u001b[0m\u001b[0m\n\u001b[0m",
      "\u001b[0;31mAttributeError\u001b[0m: 'Site' object has no attribute '__foo'"
     ]
    }
   ],
   "source": [
    "class Site:\n",
    "    def __init__(self, name, url):\n",
    "        self.name = name       # public\n",
    "        self.__url = url   # private\n",
    " \n",
    "    def who(self):\n",
    "        print('name  : ', self.name)\n",
    "        print('url : ', self.__url)\n",
    " \n",
    "    def __foo(self):          # 私有方法\n",
    "        print('这是私有方法')\n",
    " \n",
    "    def foo(self):            # 公共方法\n",
    "        print('这是公共方法')\n",
    "        self.__foo()\n",
    " \n",
    "x = Site('菜鸟教程', 'www.runoob.com')\n",
    "x.who()        # 正常输出\n",
    "x.foo()        # 正常输出\n",
    "x.__foo()      # 报错"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "类的专有方法：\n",
    "\n",
    "* \\__init__ : 构造函数，在生成对象时调用\n",
    "* \\__del__ : 析构函数，释放对象时使用\n",
    "* \\__repr__ : 打印，转换\n",
    "* \\__setitem__ : 按照索引赋值\n",
    "* \\__getitem__: 按照索引获取值\n",
    "* \\__len__: 获得长度\n",
    "* \\__cmp__: 比较运算\n",
    "* \\__call__: 函数调用\n",
    "* \\__add__: 加运算\n",
    "* \\__sub__: 减运算\n",
    "* \\__mul__: 乘运算\n",
    "* \\__div__: 除运算\n",
    "* \\__mod__: 求余运算\n",
    "* \\__pow__: 乘方\n",
    "    \n",
    "## 运算符重载\n",
    "\n",
    "Python同样支持运算符重载，我们可以对类的专有方法进行重载，实例如下："
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 13,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Vector (7, 8)\n"
     ]
    }
   ],
   "source": [
    "class Vector:\n",
    "   def __init__(self, a, b):\n",
    "      self.a = a\n",
    "      self.b = b\n",
    " \n",
    "   def __str__(self):\n",
    "      return 'Vector (%d, %d)' % (self.a, self.b)\n",
    "   \n",
    "   def __add__(self,other):\n",
    "      return Vector(self.a + other.a, self.b + other.b)\n",
    " \n",
    "v1 = Vector(2,10)\n",
    "v2 = Vector(5,-2)\n",
    "print (v1 + v2)"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.6.5"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
